package com.bee.beemanage.system.aspect.aop;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.bee.beemanage.api.entity.SysUser;
import com.bee.beemanage.api.entity.log.LogToMongo;
import com.bee.beemanage.system.common.utils.IpUtil;
import com.bee.beemanage.system.common.utils.context.CurrentUser;
import com.bee.beemanage.system.common.utils.exceptions.ExceptionUtils;
import com.bee.beemanage.system.common.utils.redis.RedisUtils;
import com.bee.beemanage.system.common.utils.result.Result;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.MathContext;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;

import static com.bee.beemanage.system.common.utils.result.ResultTool.fail;

/**
 * @author 陈国松
 */
@Aspect
@Component
public class ControllerLogAspect {

    private final Logger logger = LoggerFactory.getLogger(ControllerLogAspect.class);
    private LogToMongo logToMongo;
    @Resource
    private MongoTemplate mongoTemplate;
    @Resource
    private RedisUtils redisUtils;

    @Value("${token.expiredTime}")
    private int expiredTime;
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");

    @Pointcut("execution(public * com.bee.beemanage.api.controller.*.*(..))")
    public void ControllerLog() {
    }

    @Before("ControllerLog()")
    public void doBefore(JoinPoint joinPoint) {
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();
        // 有操作的时候对token的有效期重置为默认值
        String tokenInfo = request.getHeader("Authorization");
        if(null!=tokenInfo){
            String token = tokenInfo.replace("Bearer ", "");
            redisUtils.expire("LoginUsers:" + token, expiredTime);
        }
        //真实的ip
        String realIp = IpUtil.getIpAddr(request);
        //用户信息
        SysUser loginUser = CurrentUser.getLoginUser();
        // 记录下请求内容
        logger.info("请求链接 : " + request.getRequestURL().toString());
        logger.info("请求方法 : " + request.getMethod());
        logger.info("IP地址 : " + realIp);
        logger.info("方法 : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("请求参数 : " + JSON.toJSONString(joinPoint.getArgs(),
                SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
                SerializerFeature.WriteDateUseDateFormat));

        logToMongo.setRequestUrl(request.getRequestURL().toString());
        logToMongo.setRequestMethod(request.getMethod());
        logToMongo.setIp(realIp);
        logToMongo.setRequestUser(loginUser.getUsername());
        logToMongo.setMethod(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logToMongo.setRequestBody(JSON.toJSONString(joinPoint.getArgs(),
                SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
                SerializerFeature.WriteDateUseDateFormat));
    }

    @AfterReturning(returning = "ret", pointcut = "ControllerLog()")
    public void doAfterReturning(Object ret) {
        // 处理完请求，返回内容
        JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(ret));
        logger.info("返回内容 : " + JSON.toJSONString(jsonObject,
                SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
                SerializerFeature.WriteDateUseDateFormat));
        logToMongo.setResponseBody(JSON.toJSONString(jsonObject,
                SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
                SerializerFeature.WriteDateUseDateFormat));
    }

    @Around("ControllerLog()")
    public Object doAroundService(ProceedingJoinPoint proceedingJoinPoint) {
        logger.info("------------------Controller-Start------------------");
        logger.info("时间 : " + dateFormat.format(new Date()));
        logToMongo = new LogToMongo();
        logToMongo.setStartTime(dateFormat.format(new Date()));
        logToMongo.setLevel("info");
        Instant instStart = Instant.now();
        Object object = null;
        try {
            object = proceedingJoinPoint.proceed();
        } catch (Throwable e) {
            logger.error("异常:"+ ExceptionUtils.getFullStackTrace(e));
            logToMongo.setErrorMsg(ExceptionUtils.getFullStackTrace(e));
            logToMongo.setLevel("error");
            Result result = fail();
            result.setData(ExceptionUtils.getFullStackTrace(e));
            return result;
        }finally {
            Instant instEnd = Instant.now();
            long timeDifference = Duration.between(instStart, instEnd).toMillis();
            BigDecimal divideUnsigned = BigDecimal.valueOf(timeDifference)
                    .divide(BigDecimal.valueOf(1000L), new MathContext(2));
            logger.info("耗时:" + divideUnsigned+"s");
            logger.info("时间 : " + dateFormat.format(new Date()));
            logger.info("------------------Controller-End--------------------------");
            logToMongo.setUseTime(divideUnsigned+"s");
            logToMongo.setEndTime(dateFormat.format(new Date()));
            mongoTemplate.save(logToMongo);
        }
        return object;
    }
}